#pragma once

#include "class-internals.h"

namespace il2cpp
{
namespace icalls
{
namespace System
{
namespace System
{
namespace Diagnostics
{

struct CategoryDesc
{
	const char *name;
	const char *help;
	unsigned char id;
	signed int type : 2;
	unsigned int instance_type : 6;
	short first_counter;
};

#define PERFCTR_CAT(id,name,help,type,inst,first_counter) CATEGORY_ ## id,
#define PERFCTR_COUNTER(id,name,help,type,field)
enum {
#include "perfcounters-def.h"
	NUM_CATEGORIES
};

#undef PERFCTR_CAT
#undef PERFCTR_COUNTER
#define PERFCTR_CAT(id,name,help,type,inst,first_counter) CATEGORY_START_ ## id = -1,
#define PERFCTR_COUNTER(id,name,help,type,field) COUNTER_ ## id,
/* each counter is assigned an id starting from 0 inside the category */
enum {
#include "perfcounters-def.h"
	END_COUNTERS
};

#undef PERFCTR_CAT
#undef PERFCTR_COUNTER
#define PERFCTR_CAT(id,name,help,type,inst,first_counter)
#define PERFCTR_COUNTER(id,name,help,type,field) CCOUNTER_ ## id,
/* this is used just to count the number of counters */
enum {
#include "perfcounters-def.h"
	NUM_COUNTERS
};

struct CounterDesc
{
	const char *name;
	const char *help;
	short id;
	unsigned short offset; // offset inside Il2CppPerfCounters
	int type;
};

enum {
	FTYPE_CATEGORY = 'C',
	FTYPE_DELETED = 'D',
	FTYPE_PREDEF_INSTANCE = 'P', // an instance of a predef counter
	FTYPE_INSTANCE = 'I',
	FTYPE_DIRTY = 'd',
	FTYPE_END = 0
};

struct SharedHeader
{
	unsigned char ftype;
	unsigned char extra;
	unsigned short size;
};

struct SharedCategory
{
	SharedHeader header;
	unsigned short num_counters;
	unsigned short counters_data_size;
	int num_instances;
	/* variable length data follows */
	char name[1];
	// string name
	// string help
	// SharedCounter counters_info [num_counters]
};

struct SharedInstance
{
	SharedHeader header;
	size_t category_offset;
	/* variable length data follows */
	char instance_name[1];
	// string name
};

struct SharedCounter
{
	unsigned char type;
	uint8_t seq_num;
	/* variable length data follows */
	char name[1];
	// string name
	// string help
};

struct CatSearch
{
	Il2CppString *name;
	SharedCategory *cat;
};

/* map of PerformanceCounterType.cs */
enum {
	NumberOfItemsHEX32 = 0x00000000,
	NumberOfItemsHEX64 = 0x00000100,
	NumberOfItems32 = 0x00010000,
	NumberOfItems64 = 0x00010100,
	CounterDelta32 = 0x00400400,
	CounterDelta64 = 0x00400500,
	SampleCounter = 0x00410400,
	CountPerTimeInterval32 = 0x00450400,
	CountPerTimeInterval64 = 0x00450500,
	RateOfCountsPerSecond32 = 0x10410400,
	RateOfCountsPerSecond64 = 0x10410500,
	RawFraction = 0x20020400,
	CounterTimer = 0x20410500,
	Timer100Ns = 0x20510500,
	SampleFraction = 0x20C20400,
	CounterTimerInverse = 0x21410500,
	Timer100NsInverse = 0x21510500,
	CounterMultiTimer = 0x22410500,
	CounterMultiTimer100Ns = 0x22510500,
	CounterMultiTimerInverse = 0x23410500,
	CounterMultiTimer100NsInverse = 0x23510500,
	AverageTimer32 = 0x30020400,
	ElapsedTime = 0x30240500,
	AverageCount64 = 0x40020500,
	SampleBase = 0x40030401,
	AverageBase = 0x40030402,
	RawBase = 0x40030403,
	CounterMultiBase = 0x42030500
};

/* maps a small integer type to the counter types above */
static const int simple_type_to_type[] =
{
	NumberOfItemsHEX32, NumberOfItemsHEX64,
	NumberOfItems32, NumberOfItems64,
	CounterDelta32, CounterDelta64,
	SampleCounter, CountPerTimeInterval32,
	CountPerTimeInterval64, RateOfCountsPerSecond32,
	RateOfCountsPerSecond64, RawFraction,
	CounterTimer, Timer100Ns,
	SampleFraction, CounterTimerInverse,
	Timer100NsInverse, CounterMultiTimer,
	CounterMultiTimer100Ns, CounterMultiTimerInverse,
	CounterMultiTimer100NsInverse, AverageTimer32,
	ElapsedTime, AverageCount64,
	SampleBase, AverageBase,
	RawBase, CounterMultiBase
};

enum {
	SingleInstance,
	MultiInstance,
	CatTypeUnknown = -1
};

enum {
	ProcessInstance,
	ThreadInstance,
	CPUInstance,
	MonoInstance,
	NetworkInterfaceInstance,
	CustomInstance
};

/* map of CounterSample.cs */
struct Il2CppCounterSample {
	int64_t rawValue;
	int64_t baseValue;
	int64_t counterFrequency;
	int64_t systemFrequency;
	int64_t timeStamp;
	int64_t timeStamp100nSec;
	int64_t counterTimeStamp;
	int counterType;
};

#undef PERFCTR_CAT
#undef PERFCTR_COUNTER
#define PERFCTR_CAT(id,name,help,type,inst,first_counter) {name, help, CATEGORY_ ## id, type, inst ## Instance, CCOUNTER_ ## first_counter},
#define PERFCTR_COUNTER(id,name,help,type,field)
static const CategoryDesc
predef_categories[] = {
	/* sample runtime counter */
#include "perfcounters-def.h"
{ NULL, NULL, NUM_CATEGORIES, -1, 0, NUM_COUNTERS }
};

#undef PERFCTR_CAT
#undef PERFCTR_COUNTER
#define PERFCTR_CAT(id,name,help,type,inst,first_counter)
#define PERFCTR_COUNTER(id,name,help,type,field) {name, help, COUNTER_ ## id, offsetof (Il2CppPerfCounters, field), type},
static const CounterDesc
predef_counters[] = {
#include "perfcounters-def.h"
{ NULL, NULL, -1, 0, 0 }
};
/*
* We have several different classes of counters:
* *) system counters
* *) runtime counters
* *) remote counters
* *) user-defined counters
* *) windows counters (the implementation on windows will use this)
*
* To easily handle the differences we create a vtable for each class that contains the
* function pointers with the actual implementation to access the counters.
*/
typedef struct _ImplVtable ImplVtable;

typedef bool(*SampleFunc) (ImplVtable *vtable, bool only_value, Il2CppCounterSample* sample);
typedef uint64_t(*UpdateFunc) (ImplVtable *vtable, bool do_incr, int64_t value);
typedef void(*CleanupFunc) (ImplVtable *vtable);

struct _ImplVtable {
	void *arg;
	SampleFunc sample;
	UpdateFunc update;
	CleanupFunc cleanup;
};

struct CustomVTable
{
	ImplVtable vtable;
	SharedInstance *instance_desc;
	SharedCounter *counter_desc;
};

const CategoryDesc* find_category(Il2CppString *category);
const CounterDesc* get_counter_in_category(const CategoryDesc *desc, Il2CppString *counter);
ImplVtable* create_vtable(void *arg, SampleFunc sample, UpdateFunc update);

} /* namespace Diagnostics */
} /* namespace System */
} /* namespace System */
} /* namespace icalls */
} /* namespace il2cpp */
